<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- 设置文档字符编码和视口 -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 页面标题 -->
    <title>自由落体实验</title>
    <!-- 引入外部样式表和字体 -->
         <!-- Favicon -->
    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
    <link rel="icon" type="image/png" href="/favicon.png">
    <link rel="apple-touch-icon" href="/apple-touch-icon.png">
    <link rel="stylesheet" href="/css/style.css">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        /* 定义Material Design 3颜色系统 */
        :root {
            --md-sys-color-primary: #6750A4;        /* 主色调 */
            --md-sys-color-on-primary: #FFFFFF;     /* 主色调上的文字颜色 */
            --md-sys-color-primary-container: #EADDFF; /* 主色调容器背景 */
            --md-sys-color-on-primary-container: #21005E; /* 主色调容器上的文字颜色 */
            --md-sys-color-surface: #FEF7FF;        /* 页面背景色 */
            --md-sys-color-on-surface: #1D1B20;     /* 页面文字颜色 */
            --md-sys-color-surface-variant: #E7E0EC; /* 表面变体颜色 */
            --md-sys-color-on-surface-variant: #49454F; /* 表面变体上的文字颜色 */
            --md-sys-color-outline: #79747E;        /* 轮廓线颜色 */
            --md-sys-color-error: #B3261E;          /* 错误提示颜色 */
            --md-sys-color-success: #4CAF50;        /* 成功提示颜色 */
        }

        /* 基础页面样式 */
        body {
            font-family: 'Roboto', sans-serif;      /* 使用Roboto字体 */
            margin: 0;                              /* 移除默认边距 */
            padding: 0;                             /* 移除默认内边距 */
            background-color: var(--md-sys-color-surface); /* 设置背景色 */
            color: var(--md-sys-color-on-surface);  /* 设置文字颜色 */
            min-height: 100vh;                      /* 最小高度为视口高度 */
        }

        /* 主容器样式 */
        .container {
            max-width: 1200px;                      /* 最大宽度限制 */
            margin: 0 auto;                         /* 水平居中 */
            padding: 2rem;                          /* 内边距 */
        }

        /* 页头样式 */
        .header {
            text-align: center;                     /* 文字居中 */
            margin-bottom: 3rem;                    /* 底部外边距 */
        }

        .header h1 {
            font-size: 2.5rem;                      /* 标题字体大小 */
            font-weight: 500;                       /* 字体粗细 */
            color: var(--md-sys-color-on-surface);  /* 标题颜色 */
            margin-bottom: 1rem;                    /* 底部外边距 */
            letter-spacing: -0.5px;                 /* 字间距 */
        }

        .header p {
            font-size: 1.1rem;                      /* 段落字体大小 */
            color: var(--md-sys-color-on-surface-variant); /* 段落文字颜色 */
            max-width: 600px;                       /* 最大宽度 */
            margin: 0 auto;                         /* 水平居中 */
            line-height: 1.6;                       /* 行高 */
        }

        /* 内容区域布局 */
        .content {
            display: grid;                          /* 使用网格布局 */
            grid-template-columns: 1fr 1fr;         /* 两列等宽 */
            gap: 2rem;                              /* 列间距 */
            align-items: start;                     /* 顶部对齐 */
        }

        /* 响应式设计：移动端布局 */
        @media (max-width: 768px) {
            .content {
                grid-template-columns: 1fr;         /* 单列布局 */
            }
        }

        /* 表单容器样式 */
        .form-container {
            background-color: white;
            border-radius: 20px;
            padding: 30px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            border: 1px solid rgba(0, 0, 0, 0.05);
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        /* 表单组样式 */
        .form-group {
            margin-bottom: 0;                       /* 移除底部外边距 */
        }

        .form-group label {
            display: block;                         /* 块级显示 */
            font-size: 1rem;                        /* 字体大小 */
            font-weight: 500;                       /* 字体粗细 */
            color: var(--md-sys-color-on-surface);  /* 文字颜色 */
            margin-bottom: 0.5rem;                  /* 底部外边距 */
        }

        .form-group input {
            width: 100%;                           /* 宽度100% */
            padding: 0.75rem 1rem;                 /* 内边距 */
            border: 1px solid var(--md-sys-color-outline); /* 边框 */
            border-radius: 0.75rem;                /* 圆角 */
            font-size: 1rem;                       /* 字体大小 */
            color: var(--md-sys-color-on-surface); /* 文字颜色 */
            background-color: var(--md-sys-color-surface); /* 背景色 */
            transition: border-color 0.3s ease;    /* 过渡效果 */
            box-sizing: border-box;                /* 盒模型计算方式 */
        }

        .form-group input:focus {
            outline: none;                         /* 移除默认轮廓 */
            border-color: var(--md-sys-color-primary); /* 聚焦时边框颜色 */
        }

        /* 按钮容器样式 */
        .buttons {
            display: flex;
            gap: 16px;
            margin-top: 40px;
            flex-wrap: wrap;
        }

        /* 按钮基础样式 */
        .button {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            white-space: nowrap;
            min-width: 120px;
            padding: 16px 24px;
            border: none;
            border-radius: 30px;
            font-size: 1rem;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
            text-align: center;
            text-decoration: none;
            box-sizing: border-box;
        }

        /* 主要按钮样式 */
        .button-primary {
            background-color: #6750A4;
            color: white;
        }

        /* 次要按钮样式 */
        .button-secondary {
            background-color: #E8DEF8;
            color: #1D1B20;
        }

        /* 按钮悬停效果 */
        .button:hover {
            opacity: 0.9;                          /* 透明度 */
            transform: translateY(-2px);           /* 上移效果 */
        }

        /* 结果容器样式 */
        .result-container {
            background-color: var(--md-sys-color-surface); /* 背景色 */
            border-radius: 1.5rem;                 /* 圆角 */
            padding: 2rem;                         /* 内边距 */
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
            border: 1px solid var(--md-sys-color-surface-variant); /* 边框 */
            margin-top: 0;                         /* 顶部外边距 */
        }

        /* 结果项样式 */
        .result-item {
            display: flex;                         /* 弹性布局 */
            justify-content: space-between;        /* 两端对齐 */
            padding: 0.75rem 0;                    /* 内边距 */
            border-bottom: 1px solid var(--md-sys-color-surface-variant); /* 底部边框 */
            align-items: center;                   /* 垂直居中 */
        }

        .result-item:last-child {
            border-bottom: none;                   /* 最后一项移除底部边框 */
        }

        /* 结果标签样式 */
        .result-label {
            color: var(--md-sys-color-on-surface-variant); /* 文字颜色 */
            flex: 1;                               /* 占据剩余空间 */
        }

        /* 结果值样式 */
        .result-value {
            font-weight: 500;                      /* 字体粗细 */
            color: var(--md-sys-color-on-surface); /* 文字颜色 */
            margin-left: 1rem;                     /* 左侧外边距 */
            text-align: right;                     /* 文字右对齐 */
            min-width: 100px;                      /* 最小宽度 */
        }

        /* 动画容器样式 */
        .animation-container {
            background-color: var(--md-sys-color-surface); /* 背景色 */
            border-radius: 1.5rem;                 /* 圆角 */
            padding: 2rem;                         /* 内边距 */
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
            border: 1px solid var(--md-sys-color-surface-variant); /* 边框 */
            aspect-ratio: 16/9;                    /* 宽高比 */
            position: relative;                    /* 相对定位 */
            overflow: hidden;                      /* 溢出隐藏 */
            min-height: 400px;                     /* 最小高度 */
        }

        /* 画布样式 */
        canvas {
            width: 100%;                          /* 宽度100% */
            height: 100%;                         /* 高度100% */
            background-color: var(--md-sys-color-surface); /* 背景色 */
        }

        /* 返回按钮样式 */
        .back-button {
            display: inline-block;                 /* 行内块级显示 */
            margin-top: 2rem;                      /* 顶部外边距 */
            padding: 0.875rem 1.5rem;              /* 内边距 */
            background-color: var(--md-sys-color-surface-variant); /* 背景色 */
            color: var(--md-sys-color-on-surface-variant); /* 文字颜色 */
            text-decoration: none;                 /* 移除下划线 */
            border-radius: 1.5rem;                 /* 圆角 */
            font-weight: 500;                      /* 字体粗细 */
            transition: all 0.3s ease;             /* 过渡效果 */
        }

        /* 返回按钮悬停效果 */
        .back-button:hover {
            background-color: var(--md-sys-color-surface-variant); /* 背景色 */
            opacity: 0.9;                          /* 透明度 */
            transform: translateY(-2px);           /* 上移效果 */
        }

        /* 移动端响应式样式 */
        @media (max-width: 768px) {
            .form-container {
                padding: 1.5rem;                   /* 减小内边距 */
            }

            .animation-container {
                min-height: 300px;                 /* 减小最小高度 */
            }

            .buttons {
                flex-direction: column;            /* 垂直排列按钮 */
            }

            .button {
                width: 100%;                       /* 按钮宽度100% */
            }
        }

        /* 滑块样式 - 完全匹配截图效果 */
        .slider-container {
            margin: 40px 0;
            position: relative;
        }

        .slider-label {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
        }
        
        .slider-name {
            font-size: 1rem;
            color: var(--md-sys-color-on-surface);
            font-weight: 500;
        }
        
        .slider-value {
            font-size: 1rem;
            color: var(--md-sys-color-on-surface);
            font-weight: 400;
            text-align: right;
        }

        input[type="range"] {
            width: 100%;
            height: 6px;
            background-color: #E8DEF8; /* 淡紫色背景，对应图片中的颜色 */
            border-radius: 3px;
            -webkit-appearance: none;
            appearance: none;
            cursor: pointer;
            outline: none;
            margin: 10px 0;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 28px;
            height: 28px;
            background: #6750A4; /* 深紫色滑块手柄 */
            border-radius: 50%;
            cursor: pointer;
            border: none;
            box-shadow: 0 1px 4px rgba(0,0,0,0.2);
            margin-top: -11px; /* 调整垂直位置 */
        }
        
        input[type="range"]::-moz-range-thumb {
            width: 28px;
            height: 28px;
            background: #6750A4; /* 深紫色滑块手柄 */
            border-radius: 50%;
            cursor: pointer;
            border: none;
            box-shadow: 0 1px 4px rgba(0,0,0,0.2);
        }
        
        input[type="range"]::-moz-range-track {
            height: 6px;
            background-color: #E8DEF8;
            border-radius: 3px;
        }
    </style>
</head>
<body>
    <!-- 主容器 -->
    <div class="container">
        <!-- 页头 -->
        <div class="header">
            <h1>自由落体实验</h1>
            <p>研究物体在重力作用下的自由下落运动，计算下落时间、速度和能量变化</p>
        </div>

        <!-- 内容区域 -->
        <div class="content">
            <!-- 表单容器 -->
            <div class="form-container">
                <form id="freefallForm">
                    <!-- 高度输入组 -->
                    <div class="slider-container">
                        <div class="slider-label">
                            <div class="slider-name">初始高度 (m)</div>
                            <div class="slider-value" id="heightValue">10.0</div>
                        </div>
                        <input type="range" id="height" name="height" min="0" max="100" step="0.1" value="10">
                    </div>
                    
                    <!-- 空气阻力系数滑块 -->
                    <div class="slider-container">
                        <div class="slider-label">
                            <div class="slider-name">空气阻力系数</div>
                            <div class="slider-value" id="dragValue">0.1</div>
                        </div>
                        <input type="range" id="drag" name="drag" min="0" max="0.5" step="0.01" value="0.1">
                    </div>
                    
                    <!-- 质量滑块 -->
                    <div class="slider-container">
                        <div class="slider-label">
                            <div class="slider-name">物体质量 (kg)</div>
                            <div class="slider-value" id="massValue">1.0</div>
                        </div>
                        <input type="range" id="mass" name="mass" min="0.1" max="5" step="0.1" value="1.0">
                    </div>
                    
                    <!-- 按钮组 -->
                    <div class="buttons">
                        <button type="button" id="calculateBtn" class="button button-primary">计算落体时间(重置)</button>
                        <button type="button" id="animateBtn" class="button button-secondary">播放动画</button>
                        <a href="/" class="button button-secondary">返回主页</a>
                    </div>
                </form>

                <!-- 结果容器 -->
                <div class="result-container" id="resultContainer" style="display: none;">
                    <h3>计算结果</h3>
                    <div class="result-item">
                        <span class="result-label">下落时间</span>
                        <span class="result-value" id="timeResult">0.0 s</span>
                    </div>
                    <div class="result-item">
                        <span class="result-label">落地速度</span>
                        <span class="result-value" id="velocityResult">0.0 m/s</span>
                    </div>
                    <div class="result-item">
                        <span class="result-label">重力势能</span>
                        <span class="result-value" id="potentialEnergyResult">0.0 J</span>
                    </div>
                    <div class="result-item">
                        <span class="result-label">动能</span>
                        <span class="result-value" id="kineticEnergyResult">0.0 J</span>
                    </div>
                </div>
            </div>

            <!-- 动画容器 -->
            <div class="animation-container">
                <canvas id="freefallCanvas"></canvas>
            </div>
        </div>
    </div>

    <script th:inline="javascript">
        // 获取CSS变量的辅助函数
        const getColor = (varName) => {
            return getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
        };

        // 获取画布和上下文
        const canvas = document.getElementById('freefallCanvas');
        const ctx = canvas.getContext('2d');
        let animationId = null;                    // 动画ID
        let isAnimating = false;                   // 动画状态标志
        let currentHeight = 10;                    // 当前高度(m)
        let displayMaxHeight = 12;                 // 显示的最大高度(m)，初始为当前高度的120%
        let mass = 1.0;                           // 物体质量(kg)
        const g = 9.8;                            // 重力加速度(m/s²)
        let timeScale = 0.5;                      // 时间缩放因子
        let dragCoefficient = 0.1;                // 空气阻力系数
        let velocity = 0;                         // 当前速度(m/s)
        let time = 0;                             // 当前时间(s)
        let lastTime = 0;                         // 上一帧时间

        // 调整画布大小以适应容器
        function resizeCanvas() {
            const container = canvas.parentElement;
            canvas.width = container.clientWidth - 20;  // 减去padding
            canvas.height = container.clientHeight - 20;
        }

        // 绘制场景
        function drawScene() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制背景
            ctx.fillStyle = getColor('--md-sys-color-surface');
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // 绘制地面
            const groundY = canvas.height - 50;
            ctx.fillStyle = getColor('--md-sys-color-surface-variant');
            ctx.fillRect(0, groundY, canvas.width, 50);
            
            // 获取刻度间隔
            const scaleInterval = calculateScaleInterval(displayMaxHeight);
            
            // 绘制标尺
            ctx.strokeStyle = getColor('--md-sys-color-on-surface-variant');
            ctx.fillStyle = getColor('--md-sys-color-on-surface-variant');
            ctx.lineWidth = 1;
            ctx.font = '12px Roboto';
            ctx.textAlign = 'right';
            
            // 计算高度到像素的转换比例
            const scale = (canvas.height - 100) / displayMaxHeight;
            
            // 绘制刻度线和数值
            for (let h = 0; h <= displayMaxHeight; h += scaleInterval) {
                const y = groundY - h * scale;
                if (y >= 20) {
                    ctx.beginPath();
                    ctx.moveTo(30, y);
                    ctx.lineTo(40, y);
                    ctx.stroke();
                    
                    // 只在整数刻度处显示数值
                    ctx.fillText(h + ' m', 25, y + 5);
                }
            }
            
            // 计算物体位置 (将高度映射到画布)
            const objectY = groundY - currentHeight * scale;
            const objectSize = 20 + mass * 5;
            
            // 绘制物体
            ctx.fillStyle = getColor('--md-sys-color-primary');
            ctx.beginPath();
            ctx.arc(canvas.width / 2, objectY, objectSize / 2, 0, Math.PI * 2);
            ctx.fill();
            
            // 绘制速度矢量
            if (velocity > 0.1) {
                const arrowLength = Math.min(80, velocity * 5);
                const arrowY = objectY + objectSize / 2 + 5;
                
                ctx.strokeStyle = getColor('--md-sys-color-error');
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(canvas.width / 2, arrowY);
                ctx.lineTo(canvas.width / 2, arrowY + arrowLength);
                
                // 箭头
                ctx.lineTo(canvas.width / 2 - 5, arrowY + arrowLength - 10);
                ctx.moveTo(canvas.width / 2, arrowY + arrowLength);
                ctx.lineTo(canvas.width / 2 + 5, arrowY + arrowLength - 10);
                ctx.stroke();
                
                // 速度标签
                ctx.fillStyle = getColor('--md-sys-color-error');
                ctx.font = '12px Roboto';
                ctx.textAlign = 'left';
                ctx.fillText(velocity.toFixed(1) + ' m/s', canvas.width / 2 + 10, arrowY + arrowLength / 2);
            }
            
            // 绘制文字标签
            ctx.fillStyle = getColor('--md-sys-color-on-surface');
            ctx.font = '16px Roboto';
            ctx.textAlign = 'center';
            ctx.fillText('高度: ' + currentHeight.toFixed(1) + ' m', canvas.width / 2, 30);
        }

        // 计算合适的刻度间隔
        function calculateScaleInterval(maxHeight) {
            if (maxHeight <= 10) return 1;
            if (maxHeight <= 20) return 2;
            if (maxHeight <= 50) return 5;
            if (maxHeight <= 100) return 10;
            if (maxHeight <= 200) return 20;
            if (maxHeight <= 500) return 50;
            return 100;
        }

        // 动画函数
        function animateFall() {
            if (isAnimating) {
                cancelAnimationFrame(animationId);
                isAnimating = false;
                document.getElementById('animateBtn').textContent = '播放动画';
                drawScene();
                return;
            }
            
            isAnimating = true;
            document.getElementById('animateBtn').textContent = '停止动画';
            
            // 重置状态
            time = 0;
            velocity = 0;
            // 重置当前高度
            currentHeight = parseFloat(document.getElementById('height').value);
            // 在开始前固定显示高度，确保下落过程中刻度不变
            displayMaxHeight = Math.ceil(currentHeight * 1.2);
            
            lastTime = performance.now();
            
            function animate() {
                if (!isAnimating) return;
                
                const now = performance.now();
                const deltaTime = (now - lastTime) / 1000 * timeScale; // 转换为秒
                lastTime = now;
                
                time += deltaTime;
                
                // 自由落体计算（考虑空气阻力）
                if (dragCoefficient > 0) {
                    // 计算空气阻力 (简化模型)
                    const dragForce = 0.5 * dragCoefficient * velocity * velocity;
                    const netForce = mass * g - dragForce;
                    const acceleration = netForce / mass;
                    
                    velocity += acceleration * deltaTime;
                } else {
                    // 无空气阻力时，单纯的自由落体
                    velocity += g * deltaTime;
                }
                
                // 更新高度
                currentHeight = Math.max(0, currentHeight - velocity * deltaTime);
                
                // 检查是否落地
                if (currentHeight <= 0) {
                    currentHeight = 0;
                    velocity = 0;
                    isAnimating = false;
                    document.getElementById('animateBtn').textContent = '播放动画';
                }
                
                // 更新显示
                document.getElementById('timeResult').textContent = time.toFixed(2) + ' s';
                document.getElementById('velocityResult').textContent = velocity.toFixed(2) + ' m/s';
                document.getElementById('potentialEnergyResult').textContent = (mass * g * currentHeight).toFixed(2) + ' J';
                document.getElementById('kineticEnergyResult').textContent = (0.5 * mass * velocity * velocity).toFixed(2) + ' J';
                
                drawScene();
                
                if (isAnimating) {
                    animationId = requestAnimationFrame(animate);
                }
            }
            
            animate();
        }

        // 计算物理参数
        function calculatePhysics(height) {
            const fallTime = Math.sqrt(2 * height / g); // 下落时间
            const finalVelocity = g * fallTime;         // 最终速度
            const potentialEnergy = mass * g * height;  // 势能
            const kineticEnergy = 0.5 * mass * Math.pow(finalVelocity, 2); // 动能
            
            return {
                time: fallTime,
                velocity: finalVelocity,
                potentialEnergy: potentialEnergy,
                kineticEnergy: kineticEnergy
            };
        }

        // 页面加载和窗口大小变化事件监听
        window.addEventListener('load', function() {
            resizeCanvas();
            drawScene();
            
            // 设置初始值
            const heightInput = document.getElementById('height');
            const heightValue = document.getElementById('heightValue');
            const dragInput = document.getElementById('drag');
            const dragValue = document.getElementById('dragValue');
            const massInput = document.getElementById('mass');
            const massValue = document.getElementById('massValue');
            
            if (!heightInput.value) heightInput.value = currentHeight;
            
            // 添加滑块样式
            const sliders = document.querySelectorAll('input[type="range"]');
            sliders.forEach(slider => {
                // 只更新显示值，不设置渐变背景
                function updateSliderValue(slider) {
                    const id = slider.id;
                    const valueDisplay = document.getElementById(`${id}Value`);
                    if (valueDisplay) {
                        valueDisplay.textContent = slider.value;
                    }
                }
                
                // 初始化滑块值
                updateSliderValue(slider);
                
                // 添加滑块事件监听
                slider.addEventListener('input', function() {
                    updateSliderValue(this);
                });
            });
            
            // 实时更新预览
            heightInput.addEventListener('input', function() {
                const height = parseFloat(this.value);
                heightValue.textContent = height.toFixed(1);
                currentHeight = height;
                // 更新刻度显示范围
                displayMaxHeight = Math.ceil(currentHeight * 1.2);
                drawScene();
            });
            
            // 添加空气阻力系数滑块监听
            dragInput.addEventListener('input', function() {
                const drag = parseFloat(this.value);
                dragValue.textContent = drag.toFixed(2);
                dragCoefficient = drag;
                drawScene();
            });
            
            // 添加质量滑块监听
            massInput.addEventListener('input', function() {
                const newMass = parseFloat(this.value);
                massValue.textContent = newMass.toFixed(1);
                mass = newMass;
                drawScene();
            });
        });
        
        window.addEventListener('resize', function() {
            resizeCanvas();
            drawScene();
        });

        // 计算按钮点击事件
        document.getElementById('calculateBtn').addEventListener('click', function() {
            const height = parseFloat(document.getElementById('height').value);
            
            const results = calculatePhysics(height);
            
            // 更新结果显示
            document.getElementById('timeResult').textContent = results.time.toFixed(2) + ' s';
            document.getElementById('velocityResult').textContent = results.velocity.toFixed(2) + ' m/s';
            document.getElementById('potentialEnergyResult').textContent = results.potentialEnergy.toFixed(2) + ' J';
            document.getElementById('kineticEnergyResult').textContent = results.kineticEnergy.toFixed(2) + ' J';
            
            document.getElementById('resultContainer').style.display = 'block';
            
            currentHeight = height;
            
            drawScene();
        });

        // 动画按钮点击事件
        document.getElementById('animateBtn').addEventListener('click', function() {
            animateFall();
        });

        // 初始绘制
        const initialHeightValue = /*[[${height}]]*/ 10;
        
        currentHeight = initialHeightValue;
        
        // 页面加载时自动调整画布大小
        if (document.readyState === 'complete') {
            resizeCanvas();
            drawScene();
        } else {
            window.addEventListener('load', function() {
                resizeCanvas();
                drawScene();
            });
        }
    </script>
</body>
</html>